#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "err.hpp"
#include "Sock.hpp"
#include "Thread.hpp"
#include "Protocol.hpp"

namespace ns_client
{
    // 前置声明
    class TcpClient;

    // 线程所需要的基本信息
    struct ThreadData
    {
        ThreadData(const int &sock, const string &ip, const uint16_t &port, TcpClient *pt)
            : _sock(sock), _ip(ip), _port(port), _pt(pt)
        {
        }

        ~ThreadData()
        {
        }

        int _sock;
        std::string _ip;
        uint16_t _port;
        TcpClient *_pt; // 指向TCP对象的指针
    };

    static const std::string default_ip = "47.106.166.108";
    static const uint16_t default_port = 8888;
    static int num = 0; // 线程编号

    using func_t = function<ns_protocol::Request(const std::string&)>;

    class TcpClient
    {
    public:
        TcpClient(const func_t &func, const std::string &ip = default_ip, const uint16_t &port = default_port)
            : _func(func), _ip(ip), _port(port)
        {
        }

        ~TcpClient()
        {
            _sock.Close(); // 关闭套接字
        }

        // 初始化客户端
        void InitClient()
        {
            // 创建、绑定（系统自动绑）
            _sock.Socket();
        }

        // 启动客户端
        void StartClient()
        {
            int n = 5;
            while (n)
            {
                if (_sock.Connect(_ip, _port) >= 0)
                    break;

                std::cout << "连接服务器失败，正在重试: " << n-- << std::endl;
                sleep(1);
            }

            if (n == 0)
            {
                std::cout << "服务器连接失败!" << std::endl;
                exit(CONNECT_ERR);
            }

            ServiceIO(_sock.getFd(), _ip, _port);
        }

        // 业务处理
        void ServiceIO(int sock, const std::string &ip, const uint16_t &port)
        {
            while (true)
            {
                // 1、输入信息（表达式）
                std::string msg;
                std::cout << "Please Enter :> ";
                std::getline(std::cin, msg);

                // 2、提取信息
                ns_protocol::Request req = _func(msg); // 将信息存入 请求 类中

                // 3、序列化
                msg.clear();
                req.Serialize(&msg);

                // 4、添加报头
                msg = ns_protocol::AddHeader(msg);

                std::cout << "client sock: " << sock << endl;

                // 5、发送报文
                send(sock, msg.c_str(), msg.size(), 0);

                // 6、读取报文
                std::string inbuff; // 缓冲区，多次读取时，数据不丢失
            flag:
                std::string package; // 读取的包
                int n = ns_protocol::ReadPackage(sock, inbuff, &package);
                if (n == ns_protocol::READ_ERR)
                    break;
                else if (n == ns_protocol::READ_KEEP)
                    goto flag;
                else
                {
                    // 7、删除报头
                    package = ns_protocol::RemoveHeader(package, n);

                    // 8、反序列化
                    ns_protocol::Response res;
                    res.Deserialize(package);

                    // 9、输出结果
                    std::cout << "Result: " << res.getRet() << "[" << res.getCode() << "]" << endl;;
                }
            }
        }

    private:
        Yohifo::Sock _sock;
        std::string _ip;
        uint16_t _port;
        func_t _func;
    };
}
